"""
文件处理工具函数

提供文件验证、保存、删除等功能
"""

import os
import uuid
import hashlib
import magic
from pathlib import Path
from typing import Optional, Tuple
from fastapi import UploadFile, HTTPException, status
import sys

# 处理导入路径
if __name__ == "__main__" or "." not in __name__:
    sys.path.insert(0, str(Path(__file__).parent.parent.parent))
    from stage2_advanced.chapter03_file_upload.config import (
        UPLOAD_DIR,
        MAX_FILE_SIZE,
        USE_UUID_FILENAME,
        MAX_FILENAME_LENGTH,
        CHECK_FILE_CONTENT,
        get_upload_path,
    )
else:
    from .config import (
        UPLOAD_DIR,
        MAX_FILE_SIZE,
        USE_UUID_FILENAME,
        MAX_FILENAME_LENGTH,
        CHECK_FILE_CONTENT,
        get_upload_path,
    )


# ========== 文件验证 ==========

def validate_file_size(file: UploadFile, max_size: int = MAX_FILE_SIZE) -> None:
    """
    验证文件大小
    
    Args:
        file: 上传的文件对象
        max_size: 最大允许大小（字节）
        
    Raises:
        HTTPException: 文件过大时抛出
    """
    # 获取文件大小
    file.file.seek(0, 2)  # 移动到文件末尾
    file_size = file.file.tell()  # 获取当前位置（即文件大小）
    file.file.seek(0)  # 重置到文件开头
    
    if file_size > max_size:
        from .config import format_file_size
        raise HTTPException(
            status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
            detail=f"File too large. Max size: {format_file_size(max_size)}, "
                   f"your file: {format_file_size(file_size)}"
        )


def validate_file_extension(filename: str, allowed_extensions: set) -> None:
    """
    验证文件扩展名
    
    Args:
        filename: 文件名
        allowed_extensions: 允许的扩展名集合
        
    Raises:
        HTTPException: 扩展名不允许时抛出
    """
    ext = Path(filename).suffix.lower()
    if ext not in allowed_extensions:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"File extension '{ext}' not allowed. "
                   f"Allowed: {', '.join(allowed_extensions)}"
        )


def validate_file_content(file: UploadFile, allowed_mimetypes: set) -> None:
    """
    验证文件内容类型（通过魔数检查）
    
    Args:
        file: 上传的文件对象
        allowed_mimetypes: 允许的 MIME 类型集合
        
    Raises:
        HTTPException: 文件类型不允许时抛出
    """
    if not CHECK_FILE_CONTENT:
        return
    
    # 读取文件头部（前 2048 字节通常足够识别文件类型）
    file.file.seek(0)
    file_header = file.file.read(2048)
    file.file.seek(0)
    
    # 使用 python-magic 检测文件类型
    try:
        mime = magic.from_buffer(file_header, mime=True)
        if mime not in allowed_mimetypes:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"File type '{mime}' not allowed. "
                       f"Allowed: {', '.join(allowed_mimetypes)}"
            )
    except Exception as e:
        # 如果无法检测文件类型，拒绝上传
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Could not detect file type: {str(e)}"
        )


# ========== 文件命名 ==========

def generate_unique_filename(original_filename: str) -> str:
    """
    生成唯一的文件名
    
    Args:
        original_filename: 原始文件名
        
    Returns:
        唯一的文件名
    """
    if USE_UUID_FILENAME:
        # 使用 UUID + 原扩展名
        ext = Path(original_filename).suffix
        return f"{uuid.uuid4()}{ext}"
    else:
        # 使用原文件名 + 时间戳
        import time
        stem = Path(original_filename).stem
        ext = Path(original_filename).suffix
        timestamp = int(time.time() * 1000)
        return f"{stem}_{timestamp}{ext}"


def sanitize_filename(filename: str) -> str:
    """
    清理文件名，移除不安全字符
    
    Args:
        filename: 原始文件名
        
    Returns:
        清理后的文件名
    """
    # 移除路径分隔符和其他不安全字符
    unsafe_chars = ['/', '\\', '..', '<', '>', ':', '"', '|', '?', '*']
    safe_filename = filename
    for char in unsafe_chars:
        safe_filename = safe_filename.replace(char, '_')
    
    # 限制文件名长度
    if len(safe_filename) > MAX_FILENAME_LENGTH:
        stem = Path(safe_filename).stem[:MAX_FILENAME_LENGTH - 20]
        ext = Path(safe_filename).suffix
        safe_filename = f"{stem}{ext}"
    
    return safe_filename


# ========== 文件保存 ==========

async def save_upload_file(
    file: UploadFile,
    subdir: str = "",
    use_original_name: bool = False
) -> Tuple[str, Path]:
    """
    保存上传的文件
    
    Args:
        file: 上传的文件对象
        subdir: 子目录
        use_original_name: 是否使用原文件名
        
    Returns:
        (文件名, 完整路径) 元组
    """
    # 生成文件名
    if use_original_name:
        filename = sanitize_filename(file.filename)
    else:
        filename = generate_unique_filename(file.filename)
    
    # 获取保存路径
    file_path = get_upload_path(filename, subdir)
    
    # 确保目录存在
    file_path.parent.mkdir(parents=True, exist_ok=True)
    
    # 保存文件
    try:
        contents = await file.read()
        with open(file_path, "wb") as f:
            f.write(contents)
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to save file: {str(e)}"
        )
    finally:
        await file.seek(0)  # 重置文件指针
    
    return filename, file_path


# ========== 文件删除 ==========

def delete_file(file_path: Path) -> bool:
    """
    删除文件
    
    Args:
        file_path: 文件路径
        
    Returns:
        是否删除成功
    """
    try:
        if file_path.exists():
            file_path.unlink()
            return True
        return False
    except Exception:
        return False


# ========== 文件信息 ==========

def get_file_info(file_path: Path) -> dict:
    """
    获取文件信息
    
    Args:
        file_path: 文件路径
        
    Returns:
        文件信息字典
    """
    if not file_path.exists():
        return {}
    
    stat = file_path.stat()
    
    return {
        "filename": file_path.name,
        "size": stat.st_size,
        "size_formatted": format_file_size(stat.st_size),
        "created_at": stat.st_ctime,
        "modified_at": stat.st_mtime,
        "extension": file_path.suffix,
        "mimetype": magic.from_file(str(file_path), mime=True) if CHECK_FILE_CONTENT else None,
    }


def calculate_file_hash(file_path: Path, algorithm: str = "sha256") -> str:
    """
    计算文件哈希值
    
    Args:
        file_path: 文件路径
        algorithm: 哈希算法（md5/sha1/sha256）
        
    Returns:
        哈希值（十六进制字符串）
    """
    hash_func = getattr(hashlib, algorithm)()
    
    with open(file_path, "rb") as f:
        # 分块读取，避免大文件占用过多内存
        for chunk in iter(lambda: f.read(4096), b""):
            hash_func.update(chunk)
    
    return hash_func.hexdigest()


# ========== 辅助函数 ==========

def format_file_size(size_bytes: int) -> str:
    """
    格式化文件大小
    
    Args:
        size_bytes: 字节数
        
    Returns:
        格式化的字符串
    """
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if size_bytes < 1024.0:
            return f"{size_bytes:.2f} {unit}"
        size_bytes /= 1024.0
    return f"{size_bytes:.2f} PB"

